#include "stdafx.h"
#include "EncodedFile.h"
#include "Logger.h"

#include "Poco/Zip/Compress.h"
#include "Poco/Zip/Decompress.h"
#include "Poco/Base64Decoder.h"
#include "Poco/Base64Encoder.h"
#include "Poco/Path.h"
#include "Poco/File.h"
#include "Poco/StreamCopier.h"

#include <sstream>
#include <fstream>

namespace ExamSystem
{

    CEncodedFile::CEncodedFile(void)
    {

    }

    CEncodedFile::~CEncodedFile(void)
    {

    }

    void CEncodedFile::Clear(void)
    {
        m_fileNames.clear();
        m_encodedString.clear();
    }

    void CEncodedFile::AddPathFile(const std::string &fileName)
    {
        m_fileNames.push_back(fileName);
    }

    int CEncodedFile::GetFileCount(void) const
    {
        return (int) m_fileNames.size();
    }

    const std::string &CEncodedFile::GetFileName(int idx) const
    {
        return m_fileNames[idx];
    }

    const std::string & CEncodedFile::GetEncodedString(void) const
    {
        if (m_encodedString.empty())
            const_cast<CEncodedFile *>(this)->Encode();

        return m_encodedString;
    }

    void CEncodedFile::SetEncodedString(const std::string &str)
    {
        m_encodedString = str;
    }

    void CEncodedFile::Decode(const std::string &targetPath)
    {
        m_fileNames.clear();

        if (m_encodedString.empty())
            return;

        Poco::File target(targetPath);
        if (target.exists() && target.isFile())
        {
            systemLogger.error("Target path " + targetPath + " is a file.");
            return;
        }

        std::string tempFileName = targetPath + "temp.tmp";
        std::stringstream sstr(m_encodedString);
        Poco::Base64Decoder decoder(sstr);

        std::ofstream fout(tempFileName.c_str(), std::ios::out|std::ios::binary);
        Poco::StreamCopier::copyStream(decoder, fout);
        fout.close();

        std::ifstream fin(tempFileName.c_str(), std::ios::in|std::ios::binary);
        Poco::Zip::Decompress c(fin, targetPath);
        Poco::Zip::ZipArchive arc = c.decompressAllFiles();
        m_fileNames.clear();
        for (Poco::Zip::ZipArchive::FileInfos::const_iterator it = arc.fileInfoBegin(); it != arc.fileInfoEnd(); it++)
            m_fileNames.push_back(it->first);

        fin.close();
        Poco::File temp(tempFileName);
        temp.remove();

        //Poco::File path(targetPath);
        //path.list(m_fileNames);
    }

    void CEncodedFile::Encode(void)
    {
        m_encodedString.clear();

        if (m_fileNames.empty())
            return;

        std::stringstream sstr;
        Poco::Base64Encoder encoder(sstr);

        //std::ofstream fout("C:\\exam\\test.zip", std::ios::binary);
        //Poco::Zip::Compress c(fout, true);
        Poco::Zip::Compress c(encoder, false);
        for (std::vector<std::string>::const_iterator it = m_fileNames.begin(); it != m_fileNames.end(); ++it)
        {
            // no WILDCARD support!
            Poco::File aFile(*it);
            if (!aFile.exists())
            {
                systemLogger.error("File not found: " + *it);
            }
            else
            {
                Poco::Path anEntry(*it);
                if (aFile.isDirectory())
                {
                    anEntry.makeDirectory();
                    c.addRecursive(anEntry, Poco::Zip::ZipCommon::CL_MAXIMUM, false/* (m_fileNames.size() != 1)*/); // add root if we have more than one entry
                }
                else
                {
                    anEntry.makeFile();
                    c.addFile(anEntry, anEntry.getFileName());
                }
            }
        }
        c.close();
        encoder.close();

        m_encodedString = sstr.str();
    }

}